package ch.ethz.fcl.metrobuzz.data.od;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import ch.ethz.fcl.metrobuzz.data.MBData;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Edge;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Node;
import ch.ethz.fcl.metrobuzz.data.scene.spacetime.Trip.TripSegment;

public class SelectLinkQuery {
	private MBData data;
	private static boolean DEBUG = false;

	private List<TripSegment> passTripSegmentList = null;
	private float passTripSize = 0;

	// pass trips in each 15 min interval
	private List<TripSegment>[] passTrips = null;

	private ODMatrix odMatrix;

	private Node start, end;

	public SelectLinkQuery(MBData data) {
		this.data = data;
		this.odMatrix = new ODMatrix(data, this);
	}

	@SuppressWarnings("unchecked")
	public void update(List<Node> selectedNodes, int startTime, int timePeriod) {
		if (selectedNodes == null || selectedNodes.size() < 2){
			start = null;
			end = null;
			
			passTripSegmentList = new ArrayList<TripSegment>();
			passTrips = (ArrayList<TripSegment>[]) new ArrayList<?>[MBData.ONE_DAY_TIME
					- MBData.TRIP_STRAT_TIME];

			odMatrix.reset();
			
			return;			
		}

		start = selectedNodes.get(0);
		end = selectedNodes.get(selectedNodes.size() - 1);

		long methodStartTime = System.currentTimeMillis();

		passTripSegmentList = new ArrayList<TripSegment>();
		passTrips = (ArrayList<TripSegment>[]) new ArrayList<?>[MBData.ONE_DAY_TIME
				- MBData.TRIP_STRAT_TIME];

		odMatrix.reset();

		if (start == null || end == null || timePeriod == 0)
			return;

		passTripSize = 0;

		List<Edge> outEdgeList = start.getOutEdges();

		Map<TripSegment, Float> startTrajPassTimeMap = start
				.getTrajPassTimeMap();

		for (Edge e : outEdgeList) {
			List<TripSegment> passTrips = e.getPassTrajs(startTime, timePeriod);
			for (TripSegment ts : passTrips) {
				Float prePassTime = startTrajPassTimeMap.get(ts);

				if (prePassTime == null)
					continue;

				boolean tripValid = true;
				for (int k = 1; k < selectedNodes.size(); k++) {
					Float passTime = selectedNodes.get(k).getTrajPassTimeMap()
							.get(ts);
					if (passTime == null || passTime < prePassTime) {
						tripValid = false;
						break;
					}

					prePassTime = passTime;
				}
				if (!tripValid)
					continue;

				passTripSegmentList.add(ts);
				// since it has an uncertainty the trip pass the selected
				// link, so the size is to add the probability
				passTripSize += ts.getProbability();
			}
		}

		for (int i = 0; i < MBData.ONE_DAY_TIME - MBData.TRIP_STRAT_TIME; i++) {
			passTrips[i] = new ArrayList<TripSegment>();
			for (Edge e : outEdgeList) {
				List<TripSegment> curPassTripsIds = e.getPassTrajs(i
						+ MBData.TRIP_STRAT_TIME, 1);
				for (TripSegment ts : curPassTripsIds) {
					Float prePassTime = startTrajPassTimeMap.get(ts);

					if (prePassTime == null)
						continue;

					boolean tripValid = true;
					for (int k = 1; k < selectedNodes.size(); k++) {
						Float passTime = selectedNodes.get(k)
								.getTrajPassTimeMap().get(ts);
						if (passTime == null || passTime < prePassTime) {
							tripValid = false;
							break;
						}

						prePassTime = passTime;
					}
					if (!tripValid)
						continue;
					
					passTrips[i].add(ts);
				}
			}
		}

		odMatrix.generate();

		if (DEBUG) {
			System.out.println(passTripSegmentList.size()
					+ " trajectories pass through " + start.getId() + " and "
					+ end.getId());
			System.out.println("For start " + MBData.startTime + " period "
					+ MBData.period);

			// for (int i = 0; i < maxTime; i++)
			// System.out.println(i + " size " + passTrips[i].size());

			System.out.println("Common trajectory query time finished in "
					+ (System.currentTimeMillis() - methodStartTime) / 1000.0f
					+ "\n");
		}
	}

	public List<TripSegment> getPassTrips() {
		return passTripSegmentList;
	}

	public float getPassTripSize() {
		return passTripSize;
	}

	public List<TripSegment>[] getAllPassTrips() {
		return passTrips;
	}

	public ODMatrix getODMatrix() {
		return odMatrix;
	}

	public Node getStart() {
		return start;
	}

	public Node getEnd() {
		return end;
	}

	public MBData getData() {
		return data;
	}
}
